package com.ls.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ls.blog.dto.LikeDTO;
import com.ls.blog.dto.Recodes;
import com.ls.blog.dto.ArticleDTO;
import com.ls.blog.dto.UserDetailsIml;
import com.ls.blog.entity.Article;
import com.ls.blog.entity.ArticleTag;
import com.ls.blog.entity.Comment;
import com.ls.blog.entity.Tag;
import com.ls.blog.mapper.ArticleMapper;
import com.ls.blog.service.ArticleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ls.blog.service.ArticleTagsService;
import com.ls.blog.service.CommentService;
import com.ls.blog.service.TagService;
import com.ls.blog.utils.*;
import com.ls.blog.vo.ArticleVO;
import com.ls.blog.vo.PageVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.ls.blog.common.consts.RedisKeyConst.ARTICLE_LIKE;


/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lishun
 * @since 2022-01-14
 */
@Service
@Transactional(rollbackFor=Exception.class)  //开启事务
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleTagsService articleTagsService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private TagService tagService;

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private RedisUtil redisUtil;



    @Autowired
    RabbitTemplate rabbitTemplate;


    @Override
    public List<Article> getByTitle(String title) {
        QueryWrapper<Article> queryWrapper = new QueryWrapper<Article>();
        queryWrapper.like("title",title);
        List<Article> articleList = articleMapper.selectList(queryWrapper);
        return articleList;
    }



    @Override
    public void logicDeleteArticle(String id) {
        Article article = articleMapper.selectById(id);
        // 设置删除状态
        article.setIsDelete(1);
        // 设置删除人
        article.setUpdateTime(LocalDateTime.now());
        // 设置操作人
        article.setUserId(UserUtils.getCurrentUser().getId());
        articleMapper.updateById(article);
    }



    @Override
    public ArticleDTO getArticleById(String id) {
        ArticleDTO articleDTO = articleMapper.getByBlogId(id);
        if (Objects.nonNull(articleDTO)){
            // 更新文章浏览量
            redisUtil.hincr("ArticleViews",articleDTO.getId().toString(),1);
        }
//        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
//        // 判断是否已经点赞
//        String key = ARTICLE_LIKE+articleDTO.getId();
//        if (redisUtil.sContain(key,ipAddr)){
//            // 点过赞
//            articleDTO.setIsLike(true);
//        }else {
//            articleDTO.setIsLike(false);
//        }
        // 分为在线状态和离线状态
        String key = ARTICLE_LIKE+articleDTO.getId();
        articleDTO.setIsLike(isLike(key));
        // 封装点赞人数
        long count = redisUtil.getBitCount(key);
        articleDTO.setUpCount((int) count);
        articleDTO.setViews((Integer) redisUtil.hget("ArticleViews",articleDTO.getId().toString()));
        return articleDTO;
    }

    /**
     * 判断是否点过赞
     * @param key
     * @return
     */
    public boolean isLike(String key){
        UserDetailsIml currentUser = UserUtils.getCurrentUser();
        if (currentUser==null){
            // 离线状态 默认没有点过赞
           return false;
        }else {
            // 在线返回是否点过赞
            Integer userId = currentUser.getId();
            return redisUtil.getBit(key,Long.valueOf(userId));
        }
    }



    @Override
    public Recodes getBlogByTypeId(ArticleVO articleVO) {
        Page<Article> page = new Page<>(articleVO.getCurrent(),articleVO.getPageSize());
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        if(new Long(-1L).equals(articleVO.getId())){
           //查全部
            wrapper.eq(Article::getIsDelete,0);
            wrapper.eq(Article::getShareStatement,true);
        }else if(new Long(999L).equals(articleVO.getId())){
            // 回收站
            wrapper.eq(Article::getIsDelete,1);
        }else if(new Long(-2L).equals(articleVO.getId())){
            // 查询草稿
            wrapper.eq(Article::getIsDelete,0);
            wrapper.eq(Article::getShareStatement,false);
        }
        else {
            // 查分类id
            wrapper.eq(Article::getIsDelete,0);
            wrapper.eq(Article::getCategoryId,articleVO.getId());
            wrapper.eq(Article::getShareStatement,true);
        }
        this.page(page,wrapper);
        return PageUtils.page(page);
    }

    @Override
    public Recodes<ArticleDTO> getArticleList(PageVO pageVO) {

        Recodes<ArticleDTO> recodes = new Recodes<>();
        if (pageVO.getCurrent()<0){
            pageVO.setCurrent(1L);
        }
        recodes.setCurrent(pageVO.getCurrent());
        recodes.setCurrentSize(pageVO.getPageSize());
        long count = this.count(new LambdaQueryWrapper<Article>()
                .eq(pageVO.getCid() != null, Article::getCategoryId, pageVO.getCid())
                .eq(Article::getIsDelete, 0)
                .eq(Article::getShareStatement, 1)
        );
        List<ArticleDTO> articleDTOList = articleMapper.getArticleList(PageUtils.translate(pageVO));
        // 封装点赞量 和浏览量
        Map<Object, Object> articleViewsMap = redisUtil.hmget("ArticleViews");
        String key = null;
        for (ArticleDTO articleDTO : articleDTOList) {
            //
            key =ARTICLE_LIKE+articleDTO.getId();
            long counts = redisUtil.getBitCount(key);
            articleDTO.setUpCount((int) counts);
            Object views = articleViewsMap.get(articleDTO.getId().toString());
            if(Objects.nonNull(views)){
                articleDTO.setViews((Integer) views);
            }
        }
        recodes.setRecodes(articleDTOList);
        recodes.setTotal(count);


        // 是否还有更多
        if (recodes.getCurrentSize()==articleDTOList.size()){
            recodes.setHasMore(true);
        }

        return recodes;
    }

    @Override
    public Recodes<ArticleDTO> getArticleListByCategoryId(ArticleVO articleVO) {
        if(new Integer(-1).equals(articleVO.getId())){
            //查全部
            articleVO.setIsDelete(0);
            articleVO.setShareStatement(1);
        }else if(new Integer(999).equals(articleVO.getId())){
            // 回收站
            articleVO.setIsDelete(1);
        }else if(new Integer(-2).equals(articleVO.getId())){
            // 查询草稿
            articleVO.setIsDelete(0);
            articleVO.setShareStatement(0);
        }
        else {
            // 查分类id
            articleVO.setIsDelete(0);
            articleVO.setShareStatement(1);
            articleVO.setCategoryId(articleVO.getId());
        }
        Page<ArticleDTO> page = new Page<>(articleVO.getCurrent(),articleVO.getPageSize());
        IPage<ArticleDTO> iPage = articleMapper.getArticleListByCategoryId(page,articleVO);
        return PageUtils.page(iPage);
    }

    @Override
    @Transactional
    public void saveOrUpdateArticle(ArticleVO articleVO) {
        Article article = ObjectUtils.copyObject(articleVO, Article.class);
        // 获得当前登录用户id
        article.setUserId(UserUtils.getCurrentUser().getId());
        article.setCreateTime(LocalDateTime.now());
        // 保存或者更新文章
        this.saveOrUpdate(article);
        // 保存或者更新标签
        this.saveOrUpdateArticleTag(articleVO,article.getId());
    }




    public void saveOrUpdateArticleTag(ArticleVO articleVO, Integer articleId){
        // 修改文章则先去掉对应的文章标签中间表
        if (Objects.nonNull(articleVO.getId())){
            articleTagsService.remove(new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId,articleId));
        }
        if (articleVO.getTags()==null || articleVO.getTags().size()==0){
            return;
        }
        // 标签去重
        List<String> tags = articleVO.getTags().stream().distinct().collect(Collectors.toList());
        // 查询已经存在的标签
        List<Tag> existTags = tagService.list(new LambdaQueryWrapper<Tag>().in(Tag::getName, tags));
        // 获得已经存在的标签名
        List<String> existTagsName = existTags.stream().map(Tag::getName).collect(Collectors.toList());
        // 获得已经存在的标签id
        List<Integer> existTagId = existTags.stream().map(Tag::getId).collect(Collectors.toList());
        // 取掉已经存在的标签
        tags.removeAll(existTagsName);
        // 保存不存在的标签
        List<Tag> tagList = tags.stream().map(item -> {
            Tag tag = new Tag();
            tag.setName(item);
            return tag;
        }).collect(Collectors.toList());
        tagService.saveBatch(tagList);
        List<Integer> tagIdList = tagList.stream().map(Tag::getId).collect(Collectors.toList());
        tagIdList.addAll(existTagId);
        List<ArticleTag> articleTagList = tagIdList.stream().map(item -> {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setArticleId(articleId);
            articleTag.setTagId(item);
            return articleTag;
        }).collect(Collectors.toList());
        // 保存中间表
        articleTagsService.saveBatch(articleTagList);
    }

    @Override
    @Transactional
    public void deleteArticle(String id) {
        // 删除文章
        this.removeById(id);
        // 删除对应标签
        articleTagsService.remove(new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getArticleId,id));
        // 删除对应评论
        commentService.remove(new LambdaQueryWrapper<Comment>().eq(Comment::getArticleId,id));
    }

    @Override
    public void recoveryArticle(String id) {
        // 逻辑删除状态更新回来即可
        LambdaUpdateWrapper<Article> updateWrapper = new LambdaUpdateWrapper<Article>()
                .eq(Article::getId, Long.valueOf(id))
                .set(Article::getIsDelete, 0L);
        this.update(updateWrapper);
    }

    @Override
    public Recodes<Article> merge(PageVO pageVO) {
        IPage<Article> page = this.page(new Page<Article>(pageVO.getCurrent(), pageVO.getPageSize()), new LambdaQueryWrapper<Article>()
                .select(Article::getId, Article::getTitle, Article::getCreateTime, Article::getDescription)
                .eq(Article::getIsDelete, 0)
                .eq(Article::getShareStatement, 1)
                .orderByAsc(Article::getCreateTime)
        );
        Recodes<Article> recodes = new Recodes<>();
        recodes.setTotal(page.getTotal());
        recodes.setRecodes(page.getRecords());
        return recodes;
    }

    @Override
    public LikeDTO articleUp(Integer id) {
//        // 获得当前IP
//        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
//        // 判断是否已经点过赞
//        String key = ARTICLE_LIKE+id;
//        boolean flag = redisUtil.sContain(key, ipAddr);
//        if(flag){
//            //  点过赞取消点赞
//            redisUtil.setRemove(key,ipAddr);
//            // 数据库修改
//        }else {
//            // 没有点过赞则添加进去
//            redisUtil.sSet(key,ipAddr);
//            // 数据库添加
//        }
//        // 获得点赞数量
//        long count = redisUtil.sGetSetSize(key);

        // todo 点赞bitmap优化
        // 文章ID为大key
        String key = ARTICLE_LIKE+id;
        // 用户ID为偏移量
        Integer userId = UserUtils.getCurrentUser().getId();
        // 判断原来是否点赞成功
        boolean flag = redisUtil.getBit(key, Long.valueOf(userId));
        // 取反
        redisUtil.setBit(key, Long.valueOf(userId),!flag);
        // 返回点赞数量
        long count = redisUtil.getBitCount(key);
        rabbitTemplate.convertAndSend("articleQueue",Article.builder().id(id).likeCount((int) count).build());
        // 统一返回结果
        LikeDTO likeDTO = new LikeDTO();
        likeDTO.setCount((int) count);
        likeDTO.setFlag(flag);
        return likeDTO;
    }

    @Override
    public List<ArticleDTO> news() {
        return articleMapper.news(10);
    }

    @Override
    public List<Article> hots() {
        return this.list(new LambdaQueryWrapper<Article>()
                .select(Article::getId,Article::getLikeCount,Article::getTitle,Article::getDescription,Article::getFirstPicture)
                .eq(Article::getShareStatement,1)
                .eq(Article::getIsDelete,0)
                .orderByDesc(Article::getIsTop,Article::getLikeCount).last("limit 3"));
    }

    @Override
    public void top(Integer id) {
        articleMapper.top(id);
    }
}


